package com.rgbgame.game;

import processing.core.PApplet;

import java.awt.Color;

class Strut {
	/**
	 * 
	 */
	Color strutColor;
	
	protected final rgbGame game;
	protected Particle pA;
	protected Particle pB;
	protected float distance; // DON'T MAKE PUBLIC! Need to also change
	// distanceSqr to reset length!
	protected float distanceSqr; // optimization variable
	private double tolerance = .001f;// fractional tolerance
	private boolean draw;
	protected boolean apply;
	public int typeFlag;
	public int id;
	protected boolean breakable;
	protected float breakDist;
	protected float sumError;
	public boolean colliding;

	{
		strutColor = rgbGame.COLOR_LIGHTGRAY;
		id = -1;
		colliding = false;
		sumError = 0f;
	}

	public int getStrutColor() {
		if (strutColor.getRed() > 160 && strutColor.getGreen() < 160 && strutColor.getBlue() < 160) {
			return rgbGame.REDINDEX;
		} else if (strutColor.getRed() < 160 && strutColor.getGreen() > 160
				&& strutColor.getBlue() < 160) {
			return rgbGame.GREENINDEX;
		} else if (strutColor.getRed() < 160 && strutColor.getGreen() < 160
				&& strutColor.getBlue() > 160) {
			return rgbGame.BLUEINDEX;
		}
		return 0;
	}

	public int getID() {
		return id;
	}

	void createID() {
		id = this.game.globalStrutList.size();
		// println(id);
		this.game.globalStrutList.add(this);
	}

	public Strut(rgbGame game, Strut s) {
		this.game = game;
		pA = s.pA;
		pB = s.pB;
		distance = s.distance;
		draw = s.draw;
		apply = s.apply;
		strutColor = s.strutColor;
		typeFlag = rgbGame.NORMAL;
		distanceSqr = distance * distance;
		breakable = false;
		// createID();
	}

	public Strut(rgbGame game, Particle A, Particle B, float d) {
		this.game = game;
		pA = A;
		pB = B;
		distance = d;
		draw = true;
		apply = true;
		typeFlag = rgbGame.NORMAL;
		distanceSqr = distance * distance;
		breakable = false;
		// createID();
	}

	public Strut(rgbGame game, Particle A, Particle B, int colorIndex) {
		this.game = game;
		pA = A;
		pB = B;
		distance = A.position.distanceTo(B.position);
		draw = true;
		apply = true;
		if (colorIndex == rgbGame.REDINDEX)
			strutColor = Color.red;
		else if (colorIndex == rgbGame.GREENINDEX)
			strutColor = Color.green;
		else if (colorIndex == rgbGame.BLUEINDEX)
			strutColor = Color.blue;
		else
			strutColor = rgbGame.COLOR_LIGHTGRAY;
		typeFlag = rgbGame.PULL; // constructor only used for colored struts,
		// which shouldn't push
		distanceSqr = distance * distance;
		breakable = false;
	}

	public Strut(rgbGame game, Particle A, Particle B, int colorIndex, boolean drawMe) {
		this(game, A, B, colorIndex);
		draw = drawMe;
	}

	public Strut(rgbGame game, Particle A, Particle B) {
		this.game = game;
		pA = A;
		pB = B;
		distance = A.position.distanceTo(B.position);
		distanceSqr = distance * distance;
		draw = true;
		apply = true;
		typeFlag = rgbGame.NORMAL;
		breakable = false;
		// createID();
	}

	public void setApply(boolean tf) {
		apply = tf;
	}
	
	public void setBreakable(float _breakDist){
		breakable = true;
		breakDist = _breakDist;
	}
	
	public void setType(int t){
		typeFlag = t;
	}

	public boolean applyConstraint(rgbGame game) {
		//this.game.debugCounter++;
		
		breakable = true;
		breakDist = 1000f;//40.1f*(pA.mass*pB.mass)/(pA.mass+pB.mass);
		//breakDistSqr = distanceSqr*1.3f;
		//returns true if constraint is already satisfied
		if (!apply)
			return true;
		// debugCounter++;
		float myDistSqr = pA.position.distanceSqr(pB.position);
		/*if (breakable && myDistSqr > breakDistSqr){
			this.apply = false;
			this.draw = false;
			return true;
		}*/
		if (typeFlag == rgbGame.PULL && myDistSqr < distanceSqr) {
			return true;
		} else if (typeFlag == rgbGame.PUSH && myDistSqr > distanceSqr) {
			return true;
		}
		float dist = (float)Math.sqrt(myDistSqr);
		float alpha = distance / (dist+.001f);
		float oneminusalpha = 1.0f - alpha;
		if (false && oneminusalpha < tolerance && oneminusalpha > -tolerance) {
			return true;
		} else { // impose constraint
			float oneoversum = 1.0f / (pA.mass + pB.mass);
			float cmx = (pA.mass * pA.position.x + pB.mass * pB.position.x)
			* oneoversum;
			float cmy = (pA.mass * pA.position.y + pB.mass * pB.position.y)
			* oneoversum;
			//float cmz = (pA.mass * pA.position.z + pB.mass * pB.position.z)
			//* oneoversum;
//			float oax = pA.position.x;
//			float oay = pA.position.y;
//			float obx = pB.position.x;
//			float oby = pB.position.y;
			
			pA.position.x = (cmx * oneminusalpha + alpha * pA.position.x);
			pA.position.y = (cmy * oneminusalpha + alpha * pA.position.y);
			//pA.position.z = (float)(cmz * oneminusalpha + alpha * pA.position.z);
			pB.position.x = (cmx * oneminusalpha + alpha * pB.position.x);
			pB.position.y = (cmy * oneminusalpha + alpha * pB.position.y);
			//pB.position.z = (float)(cmz * oneminusalpha + alpha * pB.position.z);
			//pA.positionLast.x -= pA.position.x - oax;
			//pA.positionLast.y -= pA.position.y - oay;
			//pB.positionLast.x -= pB.position.x - obx;
			//pB.positionLast.y -= pB.position.y - oby;
			sumError += 2f*pA.mass*pB.mass*oneoversum*(dist-distance);
			if (sumError>breakDist||sumError<-breakDist) breakStrut(game);
			/*if (Math.abs(dist-distance) > 10) {
				game.println((dist-distance));
				game.frameRate(1f);
				this.draw();
			}*/
			//if (game.debugFlag){ game.frameRate(1); }
			//else{ game.frameRate(30); }
		}
		return false;
	}
	
	public void breakStrut(rgbGame game){
		if (draw) {
		  for (int i=0; i<100; i++){
			game.burstFXParticles(1,
				new Vector3d(.01f*(i*pA.position.x+(100-i)*pB.position.x),
						.01f*(i*pA.position.y+(100-i)*pB.position.y),0f),
				pA.position.minus(pA.positionLast), 
				strutColor, 2f, 255);
			
		  }
		}
		strutColor = Color.black;
		apply = false;
		draw = false;
		colliding = false;
	}

	public void setDraw(boolean drw) {
		draw = drw;
	}

	public void draw() {
		//game.strokeWeight(game.constrain(4f*Math.abs(sumError),0f,15f));
		sumError = 0f;
		if (!draw)
			return;
		float factor = 2 * PApplet.max(rgbGame.width, rgbGame.height);
		if ((-rgbGame.game.cameraX < (PApplet.min(pA.position.x, pB.position.x) - factor))
				|| (-rgbGame.game.cameraX > (PApplet.max(pA.position.x, pB.position.x) + factor))) {
			return;
		}
		if ((-rgbGame.game.cameraY < (PApplet.min(pA.position.y, pB.position.y) - factor))
				|| (-rgbGame.game.cameraY > (PApplet.max(pA.position.y, pB.position.y) + factor))) {
			return;
		}

		rgbGame.pApplet.stroke(strutColor.getRed(), strutColor.getGreen(), strutColor.getBlue());
		// strokeWeight(4);
		if (rgbGame.drawing3d) {
			rgbGame.pApplet.line(pA.position.x, pA.position.y, pA.position.z,
					pB.position.x, pB.position.y, pB.position.z);
		} else {
			rgbGame.pApplet.line(pA.position.x, pA.position.y, pB.position.x, pB.position.y);
		}
		rgbGame.pApplet.noStroke();

	}

	public void setLength(float newLength) {
		distance = newLength;
	}

	public Particle getPA() {
		return pA;
	}

	public Particle getPB() {
		return pB;
	}

	public void setPA(Particle A) {
		pA = A;
	}

	public void setPB(Particle B) {
		pB = B;
	}

}